package com.example.testone.uiComponents;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.util.Size;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

import com.example.testone.R;

import java.lang.ref.WeakReference;

import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;

/**
 * @Author: dreamgyf
 * @Date: 2021/8/23
 */
public class WordsBubble {

    private final int mHorizontalMargin;

    private final int mVerticalMargin;

    private final int mTextHorizontalPadding;

    private final int mTextVerticalPadding;

    private final int mArrowMargin;//控制三角形的位置

    private final int mDistanceOfArrowToEdge;//气泡框对于绑定控件的位置

    private final Context mContext;

    private int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;

    private int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;

    private WeakReference<View> mWeakAttachedView;

    private int mGravity = Gravity.TOP | Gravity.LEFT;

    private String mText;

    @ColorInt
    private int mBackgroundColor;

    public WordsBubble(Context context) {
        mContext = context;
        mHorizontalMargin = mContext.getResources().getDimensionPixelOffset(R.dimen.margin4);
        mVerticalMargin = mContext.getResources().getDimensionPixelOffset(R.dimen.margin5);
        mTextHorizontalPadding = mContext.getResources().getDimensionPixelOffset(R.dimen.margin6);
        mTextVerticalPadding = mContext.getResources().getDimensionPixelOffset(R.dimen.margin3);
        mArrowMargin = mContext.getResources().getDimensionPixelOffset(R.dimen.margin12);
        mDistanceOfArrowToEdge = ScreenUtil.dp2px(100);
        mBackgroundColor = ContextCompat.getColor(mContext, R.color.biz_wg_color_333333);
    }

    public WordsBubble width(int width) {
        mWidth = width;
        return this;
    }

    public WordsBubble height(int height) {
        mHeight = height;
        return this;
    }

    public WordsBubble gravity(int gravity) {
        mGravity = gravity;
        return this;
    }

    public WordsBubble text(String text) {
        mText = text;
        return this;
    }

    public WordsBubble backgroundColor(@ColorInt int color) {
        mBackgroundColor = color;
        return this;
    }

    public WordsBubble with(View view) {
        mWeakAttachedView = new WeakReference<>(view);
        return this;
    }

    public void show() {
        if (mWeakAttachedView == null) {
            return;
        }

        View attachedView = mWeakAttachedView.get();
        if (attachedView == null) {
            return;
        }

        Size maxSize = calculateMaxSize(attachedView, mGravity);
        View box = buildBox(maxSize, mGravity);

        int[] location = calculateLocation(box, attachedView, mGravity);

        PopupWindow popupWindow = new PopupWindow(box, box.getMeasuredWidth(), box.getMeasuredHeight());
        popupWindow.setOutsideTouchable(true);
        popupWindow.setBackgroundDrawable(new BitmapDrawable(mContext.getResources(), (Bitmap) null));
        popupWindow.setClippingEnabled(false);
        popupWindow.showAtLocation(attachedView, Gravity.LEFT | Gravity.TOP, location[0], location[1]);
    }

    private Size calculateMaxSize(View attachedView, int gravity) {
        //attachedView绑定的view
        View rootView = attachedView.getRootView();
        Log.d("GaoChang", "calculateMaxSize: "+rootView.getClass().getName());
        int[] location = new int[2];
        attachedView.getLocationOnScreen(location);
        Log.d("GaoChang", "getLocationOnScreen: "+location[0]+","+location[1]);
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                return new Size(location[0] + attachedView.getWidth() / 2 + mDistanceOfArrowToEdge, location[1] - mVerticalMargin);
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                return new Size(location[0] + attachedView.getWidth() / 2 + mDistanceOfArrowToEdge, rootView.getHeight() - location[1] - attachedView.getHeight() - mVerticalMargin);
            } else {
                return new Size(location[0] - mHorizontalMargin, 2 * Math.min(location[1] + attachedView.getHeight() / 2, rootView.getHeight() - location[1] - attachedView.getHeight() / 2));
            }
        } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                return new Size(rootView.getWidth() - location[0] - attachedView.getWidth() / 2 + mDistanceOfArrowToEdge, location[1] - mVerticalMargin);
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                return new Size(rootView.getWidth() - location[0] - attachedView.getWidth() / 2 + mDistanceOfArrowToEdge, rootView.getHeight() - location[1] - attachedView.getHeight() - mVerticalMargin);
            } else {
                return new Size(rootView.getWidth() - location[0] - attachedView.getWidth() - mHorizontalMargin, 2 * Math.min(location[1] + attachedView.getHeight() / 2, rootView.getHeight() - location[1] - attachedView.getHeight() / 2));
            }
        } else {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                return new Size(2 * Math.min(location[0] + attachedView.getWidth() / 2, rootView.getWidth() - location[0] - attachedView.getWidth() / 2), location[1] - mVerticalMargin);
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                return new Size(2 * Math.min(location[0] + attachedView.getWidth() / 2, rootView.getWidth() - location[0] - attachedView.getWidth() / 2), rootView.getHeight() - location[1] - attachedView.getHeight() - mVerticalMargin);
            } else {
                return new Size(0, 0);
            }
        }
    }

    private View buildBox(Size maxSize, int gravity) {
        LinearLayout box = new LinearLayout(mContext);

        //文字框部分
        FrameLayout tvContainer = new FrameLayout(mContext);
        tvContainer.setBackground(new BorderLayout.BorderDrawableBuilder()
                .backgroundColor(mBackgroundColor)
                .radius(mContext.getResources().getDimension(R.dimen.width2))
                .build());
        TextView textView = new TextView(mContext);
        textView.setTextColor(ContextCompat.getColor(mContext, R.color.biz_wg_color_ffffff));
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources().getDimensionPixelSize(R.dimen.textsize12));
        textView.setText(mText);
        tvContainer.addView(textView);
        FrameLayout.LayoutParams tvLp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        tvLp.setMargins(mTextHorizontalPadding, mTextVerticalPadding, mTextHorizontalPadding, mTextVerticalPadding);
        textView.setLayoutParams(tvLp);

        LinearLayout.LayoutParams tvContainerLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        tvContainerLp.weight = 1f;
        LinearLayout.LayoutParams arrowLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                TriangleView triangleView = new TriangleView(mContext, Gravity.BOTTOM);
                box.setOrientation(LinearLayout.VERTICAL);
                box.addView(tvContainer, tvContainerLp);
                box.addView(triangleView);
                arrowLp.gravity = Gravity.RIGHT;
                arrowLp.rightMargin = mArrowMargin;
                triangleView.setLayoutParams(arrowLp);
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                TriangleView triangleView = new TriangleView(mContext, Gravity.TOP);
                box.setOrientation(LinearLayout.VERTICAL);
                box.addView(triangleView);
                box.addView(tvContainer, tvContainerLp);
                arrowLp.gravity = Gravity.RIGHT;
                arrowLp.rightMargin = mArrowMargin;
                triangleView.setLayoutParams(arrowLp);
            } else {
                TriangleView triangleView = new TriangleView(mContext, Gravity.RIGHT);
                box.setOrientation(LinearLayout.HORIZONTAL);
                box.addView(tvContainer, tvContainerLp);
                box.addView(triangleView);
                arrowLp.gravity = Gravity.CENTER_VERTICAL;
                triangleView.setLayoutParams(arrowLp);
            }
        } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                TriangleView triangleView = new TriangleView(mContext, Gravity.BOTTOM);
                box.setOrientation(LinearLayout.VERTICAL);
                box.addView(tvContainer, tvContainerLp);
                box.addView(triangleView);
                arrowLp.gravity = Gravity.LEFT;
                arrowLp.leftMargin = mArrowMargin;
                triangleView.setLayoutParams(arrowLp);
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                TriangleView triangleView = new TriangleView(mContext, Gravity.TOP);
                box.setOrientation(LinearLayout.VERTICAL);
                box.addView(triangleView);
                box.addView(tvContainer, tvContainerLp);
                arrowLp.gravity = Gravity.LEFT;
                arrowLp.leftMargin = mArrowMargin;
                triangleView.setLayoutParams(arrowLp);
            } else {
                TriangleView triangleView = new TriangleView(mContext, Gravity.LEFT);
                box.setOrientation(LinearLayout.HORIZONTAL);
                box.addView(triangleView);
                box.addView(tvContainer, tvContainerLp);
                arrowLp.gravity = Gravity.CENTER_VERTICAL;
                triangleView.setLayoutParams(arrowLp);
            }
        } else {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                TriangleView triangleView = new TriangleView(mContext, Gravity.BOTTOM);
                box.setOrientation(LinearLayout.VERTICAL);
                box.addView(tvContainer, tvContainerLp);
                box.addView(triangleView);
                arrowLp.gravity = Gravity.CENTER_HORIZONTAL;
                triangleView.setLayoutParams(arrowLp);
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                TriangleView triangleView = new TriangleView(mContext, Gravity.TOP);
                box.setOrientation(LinearLayout.VERTICAL);
                box.addView(triangleView);
                box.addView(tvContainer, tvContainerLp);
                arrowLp.gravity = Gravity.CENTER_HORIZONTAL;
                triangleView.setLayoutParams(arrowLp);
            }
        }

        int widthSpec = 0;
        int heightSpec = 0;
        if (mWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
            widthSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getWidth(), View.MeasureSpec.EXACTLY);
        } else if (mWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
            widthSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getWidth(), View.MeasureSpec.AT_MOST);
        } else {
            widthSpec = View.MeasureSpec.makeMeasureSpec(mWidth, View.MeasureSpec.EXACTLY);
        }

        if (mHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
            heightSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getHeight(), View.MeasureSpec.EXACTLY);
        } else if (mHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
            heightSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getHeight(), View.MeasureSpec.AT_MOST);
        } else {
            heightSpec = View.MeasureSpec.makeMeasureSpec(mHeight, View.MeasureSpec.EXACTLY);
        }
        box.measure(widthSpec, heightSpec);

        return box;
    }

    private int[] calculateLocation(View box, View attachedView, int gravity) {
        int[] res = new int[2];

        int[] location = new int[2];
        attachedView.getLocationOnScreen(location);

        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                res[0] = location[0] + attachedView.getWidth() / 2 - box.getMeasuredWidth() + mDistanceOfArrowToEdge;
                res[1] = location[1] - mVerticalMargin - box.getMeasuredHeight();
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                res[0] = location[0] + attachedView.getWidth() / 2 - box.getMeasuredWidth() + mDistanceOfArrowToEdge;
                res[1] = location[1] + attachedView.getHeight() + mVerticalMargin;
            } else {
                res[0] = location[0] - mHorizontalMargin - box.getMeasuredWidth();
                res[1] = location[1] + attachedView.getHeight() / 2 - box.getMeasuredHeight() / 2;
            }
        } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                res[0] = location[0] + attachedView.getWidth() / 2 - mDistanceOfArrowToEdge;
                res[1] = location[1] - mVerticalMargin - box.getMeasuredHeight();
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                res[0] = location[0] + attachedView.getWidth() / 2 - mDistanceOfArrowToEdge;
                res[1] = location[1] + attachedView.getHeight() + mVerticalMargin;
            } else {
                res[0] = location[0] + attachedView.getWidth() + mHorizontalMargin;
                res[1] = location[1] + attachedView.getHeight() / 2 - box.getMeasuredHeight() / 2;
            }
        } else {
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                res[0] = location[0] + attachedView.getWidth() / 2 - box.getMeasuredWidth() / 2;
                res[1] = location[1] - mVerticalMargin - box.getMeasuredHeight();
            } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                res[0] = location[0] + attachedView.getWidth() / 2 - box.getMeasuredWidth() / 2;
                res[1] = location[1] + attachedView.getHeight() + mVerticalMargin;
            }
        }
        return res;
    }

    private static class TriangleView extends View {

        private final int mGravity;

        private final int mWidth;

        private final int mHeight;

        private final Path mPath = new Path();

        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        public TriangleView(Context context, int gravity) {
            super(context);
            mGravity = gravity;
            mWidth = context.getResources().getDimensionPixelSize(R.dimen.width5);
            mHeight = context.getResources().getDimensionPixelSize(R.dimen.width3);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(ContextCompat.getColor(context, R.color.biz_wg_color_333333));
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if ((mGravity & Gravity.TOP) != 0 || (mGravity & Gravity.BOTTOM) != 0) {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY);
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
            } else if ((mGravity & Gravity.LEFT) != 0 || (mGravity & Gravity.RIGHT) != 0) {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY);
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            mPath.reset();

            if (mGravity == Gravity.TOP) {
                mPath.moveTo(0, mHeight);
                mPath.lineTo(mWidth, mHeight);
                mPath.lineTo((float) mWidth / 2, 0);
            } else if (mGravity == Gravity.BOTTOM) {
                mPath.moveTo(0, 0);
                mPath.lineTo((float) mWidth / 2, mHeight);
                mPath.lineTo(mWidth, 0);
            } else if (mGravity == Gravity.LEFT) {
                mPath.moveTo(mHeight, 0);
                mPath.lineTo(mHeight, mWidth);
                mPath.lineTo(0, (float) mWidth / 2);
            } else if (mGravity == Gravity.RIGHT) {
                mPath.moveTo(0, 0);
                mPath.lineTo(0, mWidth);
                mPath.lineTo(mHeight, (float) mWidth / 2);
            }
            mPath.close();

            canvas.drawPath(mPath, mPaint);
        }
    }
}
